/** @file post_filter.cpp
    @brief Postscreen filter pass */

#include "post_filter.h"

using namespace G3;

//==============================================================================
// Constructors & Destructors
//==============================================================================
G3::POSTSCREEN_PASS::POSTSCREEN_PASS () {
    log = &G3::MainLog;
}

G3::POSTSCREEN_PASS::~POSTSCREEN_PASS () {
    // Just chill
}

//==============================================================================
// Sets the postprocessed screen area
//==============================================================================
bool G3::POSTSCREEN_PASS::Init (const RECTI &r) {
    boost::shared_ptr<TEXTURE> tex;
    rect = r;

    // Configure the "render to texture" class
    renderToTex.TextureDimensions = rect;
    renderToTex.Init ();

    // Just acquire a blank texture resource
    resultHandle = G3::TextureManager.GetHandle ("G3::Blank");
    tex = G3::TextureManager.GetPointer (resultHandle);
    if (!tex)
        return false;

    // If we already have a bitmap, but with wrong size, then ..
    if (tex->Bitmap && (r.Right - r.Left != tex->Bitmap->Width || r.Bottom - r.Top != tex->Bitmap->Height)) {
        // Destroy it
        //delete tex->Bitmap;
        //tex->Bitmap = NULL;
        tex->Bitmap = boost::shared_ptr<BITMAP>();
    }

    // There's no bitmap?
    if (!tex->Bitmap) {
        // Lets create one
        tex->Bitmap = boost::shared_ptr<BITMAP> (new G3::BITMAP ());

        if (!tex->Bitmap) {
            if (log)
                log->Report ("ERROR! POSTSCREEN_PASS::Init: Not enough memory for a new bitmap..\n");
            return false;
        }

        tex->Bitmap->Create (r.Right - r.Left, r.Bottom - r.Top, 32);
        tex->glBuild2D (G3::GTF_LINEAR);
    }

    // Configure the material
    if (material.textures.empty ())
        material.textures.push_back (resultHandle);

    return true;
}

//==============================================================================
// Renders the quad with filter material
//==============================================================================
void G3::POSTSCREEN_PASS::RenderQuad (float elapsedTime) {
    // Make a backup of the previous viewport settings
    int ViewPort [4];
    glGetIntegerv (GL_VIEWPORT, ViewPort);

    // Backup lighting & texturing configuration
    bool Lighting = false;
    bool Texturing = false;

    if (glIsEnabled (GL_LIGHTING)) {
        glDisable (GL_LIGHTING);
        Lighting = true;
    }

    if (!glIsEnabled (GL_TEXTURE_2D)) {
        glEnable (GL_TEXTURE_2D);
        Texturing = false;
    }

    // Enter planar mode
    glDisable (GL_DEPTH_TEST);
    glSetPlanar (RECTI (ViewPort [0], ViewPort [3], ViewPort [2], ViewPort [1]), -1.0f, 1.0f);
    glTranslated (ViewPort [0], ViewPort [3], 0);
    // Apply filter material
    material.glApply ();
    // This is the place where the uniform variables should be updated
	if (!ConnUniforms.empty ())
		SigUniforms (this, &renderToTex, elapsedTime);

    // Render the quad
    glBegin (GL_TRIANGLE_STRIP);
        glTexCoord2f (0.0f, 1.0f);
        glVertex2i (rect.Left, -rect.Top);
        glTexCoord2f (0.0f, 0.0f);
        glVertex2i (rect.Left, -rect.Bottom);
        glTexCoord2f (1.0f, 1.0f);
        glVertex2i (rect.Right, -rect.Top);
        glTexCoord2f (1.0f, 0.0f);
        glVertex2i (rect.Right, -rect.Bottom);
    glEnd ();
    // Unapply filter material
    material.glUnApply ();
    // And restore perspective mode
    glRestorePerspective ();
    glEnable (GL_DEPTH_TEST);
    // with backup configuration
    if (Lighting)
        glEnable (GL_LIGHTING);
    if (!Texturing)
        glDisable (GL_TEXTURE_2D);
}

//==============================================================================
// Performs the postprocessing pass
//==============================================================================
void G3::POSTSCREEN_PASS::Begin (float elapsedTime) {
    // Render to the texture
    renderToTex.Begin (G3::TextureManager.GetTextureID (resultHandle));
}

void G3::POSTSCREEN_PASS::End (float elapsedTime) {
    renderToTex.End ();
    // Render the new quad
    RenderQuad (elapsedTime);
}

//==============================================================================
// Sets an uniform settings handler
//==============================================================================
G3::POSTSCREEN_PASS::SIG_CONNECTION G3::POSTSCREEN_PASS::doOnSetUniforms (UNIFORMS_HANDLER aUniformHandler) {
	Assert (aUniformHandler, "POSTSCREEN_PASS::doOnSetUniforms: aUniformHandler argument is a NULL pointer");

	SIG_CONNECTION c = SigUniforms.connect (aUniformHandler);
	ConnUniforms.push_back (c);

	return c;
}
